﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Common.Utilities;

namespace Workflows.Components.TextMining.Collapse
{
    /// <summary>
    /// 
    /// </summary>
    public class SchoolNameComparer:IAttributeComparer
    {
        private readonly Dictionary<string, string> _Synonyms;
        private readonly List<string> _SchoolTypes;
        private readonly List<string> _CommonAddrNames;
        private readonly List<string> _CommonAddrRegions;
        private readonly List<string> _StopWords;

        /// <summary>
        /// 
        /// </summary>
        public SchoolNameComparer()
        {
            this._Synonyms = new Dictionary<string, string>();
            string[] lines1 = Properties.Resources.EntitySynonyms.Split(new char[] { '\n', '\r' });
            foreach (string line in lines1)
            {
                if (line.Trim().Length > 0)
                {
                    string[] parts = line.Trim().ToLower().Split(new char[] { ',' });
                    string fullName = parts[0];
                    string alterName = parts[1];
                    this._Synonyms.Add(alterName, fullName);
                }
            }

            this._SchoolTypes=new List<string>();
            this._SchoolTypes.Add("university");
            this._SchoolTypes.Add("college");
            this._SchoolTypes.Add("institute");
            this._SchoolTypes.Add("school");

            this._CommonAddrNames=new List<string>();
            this._CommonAddrNames.Add("city");
            this._CommonAddrNames.Add("state");
            this._CommonAddrNames.Add("county");
            this._CommonAddrNames.Add("national");
            this._CommonAddrNames.Add("international");

            this._CommonAddrRegions = new List<string>();
            Dictionary<string, string> stateAltNames = this.GetUSStateAbbrev();
            foreach(string stateName in stateAltNames.Keys)
            {
                if(!this._CommonAddrRegions.Contains(stateName.ToLower()))
                {
                    this._CommonAddrRegions.Add(stateName.ToLower());
                }
            }

            this._StopWords=new List<string>();
            this._StopWords.Add("of");
            this._StopWords.Add("for");
            this._StopWords.Add("the");
            this._StopWords.Add("at");
            this._StopWords.Add("&");
            this._StopWords.Add("and");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private Dictionary<string, string> GetUSStateAbbrev()
        {
            Dictionary<string, string> uSStates = new Dictionary<string, string>();
            uSStates.Add("ALABAMA", "AL");
            uSStates.Add("ALASKA", "AK");
            uSStates.Add("AMERICAN SAMOA", "AS");
            uSStates.Add("ARIZONA", "AZ");
            uSStates.Add("ARKANSAS", "AR");
            uSStates.Add("CALIFORNIA", "CA");
            uSStates.Add("COLORADO", "CO");
            uSStates.Add("CONNECTICUT", "CT");
            uSStates.Add("DELAWARE", "DE");
            uSStates.Add("DISTRICT OF COLUMBIA", "DC");
            uSStates.Add("FEDERATED STATES OF MICRONESIA", "FM");
            uSStates.Add("FLORIDA", "FL");
            uSStates.Add("GEORGIA", "GA");
            uSStates.Add("GUAM", "GU");
            uSStates.Add("HAWAII", "HI");
            uSStates.Add("IDAHO", "ID");
            uSStates.Add("ILLINOIS", "IL");
            uSStates.Add("INDIANA", "IN");
            uSStates.Add("IOWA", "IA");
            uSStates.Add("KANSAS", "KS");
            uSStates.Add("KENTUCKY", "KY");
            uSStates.Add("LOUISIANA", "LA");
            uSStates.Add("MAINE", "ME");
            uSStates.Add("MARSHALL ISLANDS", "MH");
            uSStates.Add("MARYLAND", "MD");
            uSStates.Add("MASSACHUSETTS", "MA");
            uSStates.Add("MICHIGAN", "MI");
            uSStates.Add("MINNESOTA", "MN");
            uSStates.Add("MISSISSIPPI", "MS");
            uSStates.Add("MISSOURI", "MO");
            uSStates.Add("MONTANA", "MT");
            uSStates.Add("NEBRASKA", "NE");
            uSStates.Add("NEVADA", "NV");
            uSStates.Add("NEW HAMPSHIRE", "NH");
            uSStates.Add("NEW JERSEY", "NJ");
            uSStates.Add("NEW MEXICO", "NM");
            uSStates.Add("NEW YORK", "NY");
            uSStates.Add("NORTH CAROLINA", "NC");
            uSStates.Add("NORTH DAKOTA", "ND");
            uSStates.Add("NORTHERN MARIANA ISLANDS", "MP");
            uSStates.Add("OHIO", "OH");
            uSStates.Add("OKLAHOMA", "OK");
            uSStates.Add("OREGON", "OR");
            uSStates.Add("PALAU", "PW");
            uSStates.Add("PENNSYLVANIA", "PA");
            uSStates.Add("PUERTO RICO", "PR");
            uSStates.Add("RHODE ISLAND", "RI");
            uSStates.Add("SOUTH CAROLINA", "SC");
            uSStates.Add("SOUTH DAKOTA", "SD");
            uSStates.Add("TENNESSEE", "TN");
            uSStates.Add("TEXAS", "TX");
            uSStates.Add("UTAH", "UT");
            uSStates.Add("VERMONT", "VT");
            uSStates.Add("VIRGIN ISLANDS", "VI");
            uSStates.Add("VIRGINIA", "VA");
            uSStates.Add("WASHINGTON", "WA");
            uSStates.Add("WEST VIRGINIA", "WV");
            uSStates.Add("WISCONSIN", "WI");
            uSStates.Add("WYOMING", "WY");

            return uSStates;
        }

        public AttributeComparerType ComparerType
        {
            get
            {
                return AttributeComparerType.ClassComparer;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public SqlDbType FieldDbType
        {
            get
            {
                return SqlDbType.VarChar;
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public double Compare(object value1, object value2, double penalty, SqlConnection conn, string unevenDistTableName)
        {
            if (value1 == null || value1 == DBNull.Value)
                return 0;
            if (value2 == null || value2 == DBNull.Value)
                return 0;
            string school1 = value1.ToString().ToLower();
            string school2 = value2.ToString().ToLower();
            if (school1.Trim().Length == 0 || school2.Trim().Length == 0)
                return 0;

            foreach(string altName in this._Synonyms.Keys)
            {
                Regex altRegex = new Regex(string.Format("\\b{0}\\b", altName));
                school1 = altRegex.Replace(school1, this._Synonyms[altName]);
                school2 = altRegex.Replace(school2, this._Synonyms[altName]);
            }
            Dictionary<string, double> weightedTokens1 = this.CreateWeightedTokens(school1);
            Dictionary<string, double> weightedTokens2 = this.CreateWeightedTokens(school2);
            double maxScore1 = 0;
            foreach(string token1 in weightedTokens1.Keys)
            {
                maxScore1 += weightedTokens1[token1];
            }
            double maxScore2 = 0;
            foreach (string token2 in weightedTokens2.Keys)
            {
                maxScore2 += weightedTokens1[token2];
            }
            List<string> tokens1=new List<string>();
            tokens1.AddRange(weightedTokens1.Keys);
            List<string> tokens2 = new List<string>();
            tokens1.AddRange(weightedTokens2.Keys);
            List<string> sharedTokens = StringUtil.Intersection(tokens1, tokens2, 1, 3);
            if(sharedTokens.Count==0)
                return penalty;
            double score = 0;
            foreach(string sharedToken in sharedTokens)
            {
                score += weightedTokens1[sharedToken] + weightedTokens2[sharedToken];
            }
            return score/Math.Max(maxScore1, maxScore2);
        }

        private Dictionary<string,double> CreateWeightedTokens(string schoolName)
        {
            string[] words = schoolName.Split(new char[] { ' ', ',', '-' });
            List<string> allTokens = new List<string>();
            List<string> nameTokens = new List<string>();
            List<string> typeTokens = new List<string>();
            List<string> addrTokens = new List<string>();
            List<string> decorationTokens = new List<string>();
            foreach (string word in words)
            {
                if (word.Length > 0 && !allTokens.Contains(word))
                    allTokens.Add(word);
            }
            foreach (string word in allTokens)
            {
                if (this._SchoolTypes.Contains(word) && !typeTokens.Contains(word))
                    typeTokens.Add(word);
                else if (this._CommonAddrRegions.Contains(word) && !addrTokens.Contains(word))
                    addrTokens.Add(word);
                else if (this._CommonAddrNames.Contains(word) && !decorationTokens.Contains(word))
                    decorationTokens.Add(word);
                else if (!nameTokens.Contains(word))
                    nameTokens.Add(word);
            }
            Dictionary<string, double> weightedTokens = new Dictionary<string, double>();
            if (typeTokens.Count > 0)
            {
                foreach (string token in typeTokens)
                {
                    if (!weightedTokens.ContainsKey(token))
                        weightedTokens.Add(token, 1.0);
                }
            }
            if (addrTokens.Count > 0)
            {
                foreach (string token in addrTokens)
                {
                    if (!weightedTokens.ContainsKey(token))
                        weightedTokens.Add(token, 1.0);
                }
            }
            if (decorationTokens.Count > 0)
            {
                foreach (string token in decorationTokens)
                {
                    if (!weightedTokens.ContainsKey(token))
                        weightedTokens.Add(token, 1.0);
                }
            }
            double weight = 1.0;
            double decrement = 1.0 / allTokens.Count;
            foreach (string schoolType in this._SchoolTypes)
            {
                for (int i = 0; i < allTokens.Count; i++)
                {
                    if (allTokens[i] == schoolType)
                    {
                        if (i > 0)
                        {
                            for (int j = 0; j < i; j++)
                            {
                                if (!weightedTokens.ContainsKey(allTokens[j]))
                                    weightedTokens.Add(allTokens[j], weight);
                            }
                        }
                        if (i < allTokens.Count - 1)
                        {
                            for (int j = i + 1; j < allTokens.Count; j++)
                            {
                                if (!weightedTokens.ContainsKey(allTokens[j]))
                                    weightedTokens.Add(allTokens[j], weight);
                                weight -= decrement;
                            }
                        }
                    }
                }
            }
            return weightedTokens;
        }

        public object Merge(List<object> values, SqlConnection conn, string unevenDistTableName)
        {
            object mode = null;
            int maxFreq = 0;
            Dictionary<object, int> freqs = new Dictionary<object, int>();
            foreach (object val in values)
            {
                if (val == null || val.ToString().Length == 0)
                    continue;

                if (freqs.ContainsKey(val))
                    freqs[val] = freqs[val] + 1;
                else
                    freqs.Add(val, 1);
            }
            foreach (object val in freqs.Keys)
            {
                if (freqs[val] > maxFreq)
                {
                    maxFreq = freqs[val];
                    mode = val;
                }
            }
            return mode;
        }
    }
}
